import { BaseAgent } from '../base-agent';
import { OpenRouterClient, DEFAULT_MODEL } from '../openrouter-client';
import { extractJsonFromText } from './json-utils';

interface PlannerAction {
  action_id: string;
  description?: string;
  type?: string;
  agent_id?: string;
  executor_id?: string;
  goal?: string;
  [key: string]: unknown;
}

interface NormalizedAction extends PlannerAction {
  description: string;
  type: string;
  agent_id: string;
  executor_id: string;
  goal: string;
}

export interface WorkflowState {
  userQuery: string;
  plan?: { actions: NormalizedAction[] } | null;
  executorOutputs: Array<{
    action_id: string;
    result: unknown;
    success: boolean;
    error?: string;
  }>;
  finalResponse?: string;
  error?: string;
}

interface AgentRegistryEntry {
  definition: any;
  baseAgent: BaseAgent;
  defaultModel: string;
  defaultTemperature: number;
}

interface LangGraphRunResult {
  output: string;
  finalOutput: string;
  toolProcessing: {
    toolCalls: any[];
    toolResults: any[];
    appendedText: string;
  };
}

export class LangGraphWorkflow {
  private agentRegistry = new Map<string, AgentRegistryEntry>();
  private workflowConfig: any;
  private useMCP = false;
  private mcpServers: Record<string, any> | undefined;
  private toolsEnabled = false;
  private enablingPromise: Promise<void> | null = null;
  private apiKey = '';
  private modelOverride = '';
  private previousOutput = '';
  private currentActionContext: { actionId?: string } | null = null;
  private actionLogCallback: ((event: {
    actionId: string;
    step: number;
    thought: string;
    observation?: string;
    toolCalls?: any[];
    final?: boolean;
  }) => void) | null = null;
  private finalResponseCallback: ((text: string, status: 'partial' | 'complete' | 'fail') => void) | null = null;

  constructor(agentConfig: any, workflowConfig: any) {
    this.workflowConfig = workflowConfig || {};
    this.useMCP = Boolean(agentConfig?.mcp?.enabled);
    this.mcpServers = agentConfig?.mcp?.servers;
    this.initializeAgents(agentConfig?.agents || []);
  }

  async initialize(): Promise<void> {
    await this.ensureToolsEnabled();
  }

  async getAgentToolSummary(): Promise<Array<{ agentId: string; label: string; enabled: boolean; tools: string[] }>> {
    const summary: Array<{ agentId: string; label: string; enabled: boolean; tools: string[] }> = [];

    for (const [agentId, entry] of this.agentRegistry.entries()) {
      let tools: string[] = [];
      try {
        tools = entry.baseAgent?.mcpClient?.getAvailableTools?.() || [];
      } catch (error) {
        console.warn(`[LangGraph] 获取代理 ${agentId} 工具列表失败:`, error);
      }

      summary.push({
        agentId,
        label: entry.definition?.name || agentId,
        enabled: Boolean(entry.baseAgent?.toolsEnabled && tools.length > 0),
        tools
      });
    }

    return summary;
  }

  private initializeAgents(definitions: any[]): void {
    for (const definition of definitions) {
      const model = definition.llm?.model || DEFAULT_MODEL;
      const temperature = definition.llm?.temperature ?? 0.7;
      const client = new OpenRouterClient({
        model,
        defaultParams: { temperature }
      });

      const baseAgent = new BaseAgent(client);

      if (this.useMCP && this.mcpServers) {
        const mcpConfig = this.mcpServers['streamable-mcp-server'];
        if (mcpConfig) {
          baseAgent.setMCP(mcpConfig);
        }
      }

      this.agentRegistry.set(definition.id, {
        definition,
        baseAgent,
        defaultModel: model,
        defaultTemperature: temperature
      });
    }
  }

  private async ensureToolsEnabled(): Promise<void> {
    if (!this.useMCP) {
      return;
    }

    if (this.toolsEnabled) {
      return;
    }

    if (this.enablingPromise) {
      await this.enablingPromise;
      return;
    }

    this.enablingPromise = (async () => {
      try {
        const enablePromises: Promise<void>[] = [];
        for (const [, entry] of this.agentRegistry) {
          if (entry.baseAgent?.mcpClient) {
            enablePromises.push(entry.baseAgent.enableTools().catch((error: any) => {
              console.warn(`[LangGraph] Agent ${entry.definition.id} 启用 MCP 失败:`, error);
            }));
          }
        }
        await Promise.all(enablePromises);
        this.toolsEnabled = true;
      } finally {
        this.enablingPromise = null;
      }
    })();

    await this.enablingPromise;
  }

  updateLLMConfig(config: { apiKey?: string; modelOverride?: string }) {
    if (config.apiKey !== undefined) {
      this.apiKey = config.apiKey;
    }
    if (config.modelOverride !== undefined) {
      this.modelOverride = config.modelOverride;
    }

    for (const [, entry] of this.agentRegistry) {
      const resolvedModel = this.modelOverride || entry.definition.llm?.model || entry.defaultModel;
      entry.baseAgent?.llmClient?.updateConfig({
        apiKey: this.apiKey,
        model: resolvedModel,
        defaultParams: { temperature: entry.defaultTemperature }
      });
    }
  }

  resetContext() {
    this.previousOutput = '';
  }

  setActionLogCallback(callback?: (event: {
    actionId: string;
    step: number;
    thought: string;
    observation?: string;
    toolCalls?: any[];
    final?: boolean;
  }) => void) {
    this.actionLogCallback = callback ?? null;
  }

  setFinalResponseCallback(callback?: (text: string, status: 'partial' | 'complete' | 'fail') => void) {
    this.finalResponseCallback = callback ?? null;
  }

  async *streamExecute(userQuery: string): AsyncGenerator<Partial<WorkflowState>, void, unknown> {
    const state: WorkflowState = {
      userQuery,
      executorOutputs: []
    };

    await this.ensureToolsEnabled();

    try {
      // 1. Planner
      const plannerResult = await this.runPlanner(userQuery);
      this.previousOutput = plannerResult.finalOutput;
      state.plan = plannerResult.plan;
      yield { plan: plannerResult.plan };

      const actions = plannerResult.plan?.actions || [];

      // 2. Executor loop
      for (const action of actions) {
        try {
          const execution = await this.runAction(action, userQuery);
          state.executorOutputs.push(execution);
          this.previousOutput = typeof execution.result === 'string'
            ? execution.result
            : JSON.stringify(execution.result, null, 2);
          yield { executorOutputs: [...state.executorOutputs] };
        } catch (error: any) {
          const failure = {
            action_id: action.action_id,
            result: null,
            success: false,
            error: error?.message || '执行失败'
          };
          state.executorOutputs.push(failure);
          yield { executorOutputs: [...state.executorOutputs] };
          throw error;
        }
      }

      // 3. Supervisor
      const supervisorResult = await this.runSupervisor(userQuery, plannerResult.finalOutput, state.executorOutputs);
      this.previousOutput = supervisorResult.finalOutput;
      state.finalResponse = supervisorResult.finalOutput;
      yield { finalResponse: supervisorResult.finalOutput };

    } catch (error: any) {
      console.error('[LangGraph] 工作流执行失败:', error);
      state.error = error?.message || '执行失败';
      this.finalResponseCallback?.(state.error, 'fail');
      yield { error: state.error };
    }
  }

  private async runPlanner(userQuery: string): Promise<{ plan: { actions: NormalizedAction[] } | null; finalOutput: string }> {
    const plannerId = this.workflowConfig?.planner_id;
    if (!plannerId) {
      throw new Error('未配置 planner_id');
    }

    const conversation = [{ role: 'user', content: userQuery }];
    console.log(`[LangGraph][planner:${plannerId}] 输入:`, conversation[0]);
    const result = await this.runAgent(plannerId, conversation);
    console.log(`[LangGraph][planner:${plannerId}] 原始输出:`, result.finalOutput);

    const planJson = extractJsonFromText<any>(result.finalOutput, 'actions');
    const actions = Array.isArray(planJson?.actions) ? this.normalizeActions(planJson.actions) : [];

    if (!actions.length) {
      console.warn('[LangGraph] Planner 未返回有效的动作列表');
    }

    return {
      plan: { actions },
      finalOutput: result.finalOutput
    };
  }

  private async runAction(action: NormalizedAction, userQuery: string) {
    const agentId = this.resolveAgentId(action);
    const conversation = [
      {
        role: 'user',
        content: action.goal || action.description || userQuery
      }
    ];

    this.currentActionContext = { actionId: action.action_id };
    console.log(`[LangGraph][executor:${agentId}] 准备执行动作 ${action.action_id}:`, conversation[0]);
    try {
      const result = await this.runAgent(agentId, conversation);
      console.log(`[LangGraph][executor:${agentId}] 输出:`, result.finalOutput);
      if (result.toolProcessing?.toolCalls?.length) {
        console.log(`[LangGraph][executor:${agentId}] 工具调用:`, result.toolProcessing.toolCalls);
      }

      if (typeof result.finalOutput === 'string') {
        this.previousOutput = result.finalOutput;
      } else if (result.finalOutput) {
        try {
          this.previousOutput = JSON.stringify(result.finalOutput, null, 2);
        } catch (_) {
          this.previousOutput = String(result.finalOutput);
        }
      }

      return {
        action_id: action.action_id,
        result: result.finalOutput,
        success: true,
        toolCalls: result.toolProcessing.toolCalls,
        toolResults: result.toolProcessing.toolResults
      };
    } finally {
      this.currentActionContext = null;
    }
  }

  private async runSupervisor(userQuery: string, plannerOutput: string, executorOutputs: Array<{ action_id: string; result: unknown }>) {
    const supervisorId = this.workflowConfig?.supervisor_id;
    if (!supervisorId) {
      throw new Error('未配置 supervisor_id');
    }

    const summary = executorOutputs
      .map((output, index) => {
        const content = typeof output.result === 'string'
          ? output.result
          : JSON.stringify(output.result, null, 2);
        return `动作 ${index + 1} (${output.action_id}):\n${content}`;
      })
      .join('\n\n');

    console.log('[LangGraph][supervisor] 收集执行结果数量:', executorOutputs.length);
    if (summary) {
      console.log('[LangGraph][supervisor] 汇总执行结果预览:', summary.slice(0, 800));
    } else {
      console.log('[LangGraph][supervisor] 没有可用的执行结果传递给监督代理');
    }

    const conversation = [
      { role: 'user', content: userQuery },
      { role: 'assistant', content: plannerOutput }
    ];

    if (summary) {
      conversation.push({ role: 'assistant', content: `执行结果概览:\n${summary}` });
      this.previousOutput = summary;
    }

    const result = await this.runAgent(supervisorId, conversation, {
      onPartial: (text) => {
        if (text) {
          this.finalResponseCallback?.(text, 'partial');
        }
      }
    });
    this.finalResponseCallback?.(result.finalOutput, 'complete');
    return result;
  }

  private resolveAgentId(action: NormalizedAction): string {
    if (action.agent_id && this.agentRegistry.has(action.agent_id)) {
      return action.agent_id;
    }

    if (action.executor_id && this.agentRegistry.has(action.executor_id)) {
      return action.executor_id;
    }

    const mapping = this.workflowConfig?.executor_mapping || {};
    const mapped = mapping[action.type] || mapping.default;
    if (mapped && this.agentRegistry.has(mapped)) {
      return mapped;
    }

    throw new Error(`未找到可用的执行代理，动作类型: ${action.type}`);
  }

  private normalizeActions(actions: PlannerAction[]): NormalizedAction[] {
    return actions.map((raw, index) => {
      const fallbackDescription = typeof raw.goal === 'string' && raw.goal.trim() ? raw.goal.trim() : `执行动作 ${raw.action_id || index + 1}`;
      const description = typeof raw.description === 'string' && raw.description.trim() ? raw.description.trim() : fallbackDescription;
      const rawType = raw.type || (raw as any).category || 'default';
      const type = typeof rawType === 'string' ? rawType.trim() || 'default' : 'default';
      const action_id = String(raw.action_id || `action-${index + 1}`);
      const agentId = (raw.agent_id || raw.executor_id || this.workflowConfig?.executor_mapping?.[type] || this.workflowConfig?.executor_mapping?.default || '').trim();
      if (!agentId) {
        throw new Error(`无法解析动作 ${action_id} 的执行代理`);
      }

      return {
        ...raw,
        action_id,
        description,
        type,
        agent_id: agentId,
        executor_id: agentId,
        goal: description
      } as NormalizedAction;
    });
  }

  private buildAgentMessages(definition: any, conversation: Array<{ role: string; content: string }>): Array<{ role: string; content: string }> {
    const systemSegments = Array.isArray(definition.system_prompt)
      ? definition.system_prompt.filter((segment: unknown): segment is string => typeof segment === 'string')
      : [];

    const now = new Date();
    const currentTime = now.toLocaleString('zh-CN', {
      hour12: false,
      timeZone: 'Asia/Shanghai',
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit'
    });

    const replacements: Record<string, string> = {
      CURRENT_TIME: currentTime,
      PREVIOUS_AGENT_OUTPUTS: this.previousOutput || '暂无执行结果。'
    };

    const replaceTokens = (segment: string) => {
      let result = segment;
      for (const [token, value] of Object.entries(replacements)) {
        const pattern = new RegExp(`\\{${token}\\}`, 'g');
        result = result.replace(pattern, value);
      }
      return result;
    };

    const messages: Array<{ role: string; content: string }> = [];

    if (systemSegments.length) {
      messages.push({
        role: 'system',
        content: systemSegments.map(replaceTokens).join('\n')
      });
    }

    messages.push(...conversation);
    return messages;
  }

  private async runAgent(
    agentId: string,
    conversation: Array<{ role: string; content: string }>,
    options: { onPartial?: (text: string) => void } = {}
  ): Promise<LangGraphRunResult> {
    const entry = this.agentRegistry.get(agentId);
    if (!entry) {
      throw new Error(`未找到 Agent: ${agentId}`);
    }

    if (entry.definition?.react?.enabled) {
      return this.runAgentReact(entry, conversation, entry.definition.react, options);
    }

    const runOptions = options ? { ...options, logToolCalls: true } : { logToolCalls: true };
    return this.runAgentOnce(entry, conversation, runOptions);
  }

  private async runAgentReact(
    entry: AgentRegistryEntry,
    conversation: Array<{ role: string; content: string }>,
    reactConfig: { enabled?: boolean; max_steps?: number },
    options: { onPartial?: (text: string) => void } = {}
  ): Promise<LangGraphRunResult> {
    const maxSteps = Number.isFinite(reactConfig?.max_steps)
      ? Math.max(1, reactConfig!.max_steps!)
      : 6;

    const workingConversation = [...conversation];
    let latestResult: LangGraphRunResult | null = null;
    const actionId = this.currentActionContext?.actionId;

    for (let step = 0; step < maxSteps; step += 1) {
      console.log(`[LangGraph][react:${entry.definition.id}] 第 ${step + 1} 轮 Thought 输入:`, workingConversation);
      const result = await this.runAgentOnce(entry, workingConversation, options);
      latestResult = result;

      console.log(`[LangGraph][react:${entry.definition.id}] 第 ${step + 1} 轮 输出:`, result.finalOutput);
      const toolCalls = result.toolProcessing?.toolCalls || [];
      if (!toolCalls.length) {
        if (actionId) {
          this.actionLogCallback?.({
            actionId,
            step: step + 1,
            thought: result.finalOutput,
            final: true
          });
        }
        return result;
      }

      const observation = result.toolProcessing.appendedText || 'Observation: 工具已执行但未返回内容。';
      console.log(`[LangGraph][react:${entry.definition.id}] 第 ${step + 1} 轮 工具调用:`, toolCalls);

      if (actionId) {
        this.actionLogCallback?.({
          actionId,
          step: step + 1,
          thought: result.output,
          observation,
          toolCalls
        });
      }

      workingConversation.push({ role: 'assistant', content: result.output });
      workingConversation.push({ role: 'user', content: `Observation:\n${observation}` });
    }

    if (latestResult) {
      const warning = `${latestResult.finalOutput}\n\n⚠️ 已达到 ${maxSteps} 轮循环，可能需要人工检查。`;
      if (actionId) {
        this.actionLogCallback?.({
          actionId,
          step: maxSteps,
          thought: warning,
          final: true
        });
      }
      return {
        ...latestResult,
        finalOutput: warning
      };
    }

    throw new Error('ReAct 执行失败：未得到模型输出');
  }

  private async runAgentOnce(
    entry: AgentRegistryEntry,
    conversation: Array<{ role: string; content: string }>,
    options: { onPartial?: (text: string) => void; logToolCalls?: boolean } = {}
  ): Promise<LangGraphRunResult> {
    const messages = this.buildAgentMessages(entry.definition, conversation);

    if (!entry.baseAgent?.llmClient?.apiKey) {
      throw new Error('缺少 OpenRouter API Key');
    }

    const params: Record<string, unknown> = {};
    if (typeof entry.definition.llm?.temperature === 'number') {
      params.temperature = entry.definition.llm.temperature;
    }

    let output = '';
    for await (const chunk of entry.baseAgent.streamResponse(messages, params)) {
      output += chunk;
      options.onPartial?.(output);
    }

    const actionIdForLogging = options.logToolCalls ? this.currentActionContext?.actionId : undefined;
    const shouldLogTools = Boolean(options.logToolCalls && actionIdForLogging && this.actionLogCallback);
    const toolProcessing = await entry.baseAgent.processToolCalls(
      output,
      shouldLogTools
        ? {
            onToolCall: ({ index, toolCall, result, error, suppressObservation }) => {
              if (!actionIdForLogging || !this.actionLogCallback || suppressObservation) {
                return;
              }

              const observation = error
                ? `❌ ${error}`
                : this.formatToolObservation(result);

              this.actionLogCallback({
                actionId: actionIdForLogging,
                step: index + 1,
                thought: '',
                observation,
                toolCalls: toolCall ? [toolCall] : undefined
              });
            }
          }
        : undefined
    ) || {
      toolCalls: [],
      toolResults: [],
      appendedText: ''
    };
    const finalOutput = entry.baseAgent.getText(output, toolProcessing);
    options.onPartial?.(finalOutput);

    if (shouldLogTools && actionIdForLogging && this.actionLogCallback) {
      const finalThought = output.trim() || finalOutput;
      this.actionLogCallback({
        actionId: actionIdForLogging,
        step: (toolProcessing.toolCalls?.length || 0) + 1,
        thought: finalThought,
        final: true
      });
    }

    return {
      output,
      finalOutput,
      toolProcessing
    };
  }

  private formatToolObservation(payload: unknown): string {
    if (payload === null || payload === undefined) {
      return '（无返回内容）';
    }

    if (typeof payload === 'string' && payload.includes('🔍 检查输入框状态')) {
      return payload;
    }

    if (typeof payload === 'string') {
      return payload;
    }

    try {
      return '```json\n' + JSON.stringify(payload, null, 2) + '\n```';
    } catch (error) {
      console.warn('[LangGraph] 工具结果格式化失败:', error);
      return String(payload);
    }
  }
}
